//
//  RNTKFileSystem.m
//  ios
//
//  Created by 程巍巍 on 5/9/17.
//  Copyright © 2017 程巍巍. All rights reserved.
//

#import "RNTKFileSystem.h"

@implementation RNTKFileSystem

RCT_EXPORT_MODULE(LittoFileSystem)

- (NSDictionary<NSString *, id> *)constantsToExport
{
    NSMutableDictionary* constants = [NSMutableDictionary new];
    constants[@"HOME"] = NSHomeDirectory();
    constants[@"TEMP"] = NSTemporaryDirectory();

    return constants;
}

//NativeFS.read
//NativeFS.write
//NativeFS.stat
//NativeFS.isFile
//NativeFS.mkdir
//NativeFS.touch
//NativeFS.exists
//NativeFS.remove

static const char* HexTable = "0123456789abcdef";
RCT_REMAP_METHOD(read, read:(NSString*)path withOffset:(NSUInteger)offset maxLength:(NSUInteger)length resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {

    NSFileHandle* handle = [NSFileHandle fileHandleForReadingAtPath:path];
    [handle seekToFileOffset:offset];
    NSData* data = [handle readDataOfLength:length];
    uint8_t* buffer = data.bytes;

    NSMutableString* mstr = [[NSMutableString alloc] initWithCapacity:length*2];
    int i = 0;
    while (i < data.length) {
        uint8_t code = buffer[i++];
        [mstr appendFormat:@"%c%c", HexTable[(code >> 4) & 0xf], HexTable[code & 0xf]];
    }

    resolve(mstr);
}

static const uint8_t HexDetable[] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,2,3,4,5,6,7,8,9,0,0,0,0,0,0,0,10,11,12,13,14,15,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,10,11,12,13,14,15};
RCT_REMAP_METHOD(write, write:(NSString*)path withOffset:(NSUInteger)offset buffer:(NSString*)hex resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {

    uint8_t* buffer = malloc(hex.length/2);
    int i = 0;
    while (i < hex.length) {
        int h = HexDetable[[hex characterAtIndex:i++]];
        int l = HexDetable[[hex characterAtIndex:i++]];
        buffer[i/2-1] = ((h << 4) & 0xf0) | (l & 0xf);
    }

    NSFileHandle* handle = [NSFileHandle fileHandleForWritingAtPath:path];
    [handle seekToFileOffset:offset];
    [handle writeData:[NSData dataWithBytesNoCopy:buffer length:hex.length/2 freeWhenDone:true]];
    [handle synchronizeFile];
    [handle closeFile];

    resolve(@(hex.length/2));
}
RCT_REMAP_METHOD(stat, stat:(NSString*)path resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
    NSError* error;
    NSDictionary* attributes = [[NSFileManager defaultManager] attributesOfItemAtPath:path error:&error];
    if (error) reject(@(error.code).description, error.domain, error);
    else {
        NSMutableDictionary* result = [NSMutableDictionary new];
        result[@"type"] = [NSFileTypeRegular isEqualToString:attributes[NSFileType]] ? @"file" : [NSFileTypeDirectory isEqualToString:attributes[NSFileType]] ? @"directory" : @"unknown";
        result[@"size"] = attributes[NSFileSize];
        result[@"ctime"] = @([attributes[NSFileCreationDate] timeIntervalSince1970]);
        result[@"mtime"] = @([attributes[NSFileModificationDate] timeIntervalSince1970]);

        resolve(result);
    };
}

RCT_REMAP_METHOD(isFile, isFile:(NSString*)path resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
    BOOL isDirectory = false;
    resolve(@([[NSFileManager defaultManager] fileExistsAtPath:path isDirectory:&isDirectory] && !isDirectory));
}

RCT_REMAP_METHOD(mkdir, mkdir:(NSString*)path autoCreateParent:(BOOL)autoCreateParent resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
    NSError* error;
    BOOL status = [[NSFileManager defaultManager] createDirectoryAtPath:path withIntermediateDirectories:autoCreateParent attributes:nil error:&error];
    if (error) reject(@(error.code).description, error.domain, error);
    else resolve(@(status));
}

RCT_REMAP_METHOD(touch, touch:(NSString*)path autoCreateParent:(BOOL)autoCreateParent resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
    NSString* dirname = [path stringByDeletingLastPathComponent];
    NSError* error;
    BOOL status = [[NSFileManager defaultManager] createDirectoryAtPath:dirname withIntermediateDirectories:autoCreateParent attributes:nil error:&error];
    if (error) reject(@(error.code).description, error.domain, error);
    else {
        BOOL status = [[NSFileManager defaultManager] createFileAtPath:path contents:nil attributes:nil];
        resolve(@(status));
    }
}

RCT_REMAP_METHOD(exists, exists:(NSString*)path resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
    resolve(@([[NSFileManager defaultManager] fileExistsAtPath:path]));
}

RCT_REMAP_METHOD(remove, remove:(NSString*)path resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
    NSError* error;
    BOOL status = [[NSFileManager defaultManager] removeItemAtPath:path error:&error];
    if (error) reject(@(error.code).description, error.domain, error);
    else resolve(@(status));
}

RCT_REMAP_METHOD(readDir, readDir:(NSString*)path recusive:(BOOL)recusive resolve:(RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) {
    NSError* error;
    NSDictionary* items = recusive ? [[NSFileManager defaultManager] subpathsOfDirectoryAtPath:path error:&error] : [[NSFileManager defaultManager] contentsOfDirectoryAtPath:path error:&error];
    if (error) reject(@(error.code).description, error.domain, error);
    else resolve(items ? items : @[]);
}

@end
